﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using HslCommunication.BasicFramework;
using HslCommunication.LogNet;
using HslCommunication.MQTT;
using HslCommunication.Reflection;

namespace HslCommunication.Enthernet
{
	/// <summary>
	/// 一个支持完全自定义的Http服务器，支持返回任意的数据信息，方便调试信息，详细的案例请查看API文档信息<br />
	/// A Http server that supports fully customized, supports returning arbitrary data information, which is convenient for debugging information. For detailed cases, please refer to the API documentation information
	/// </summary>
	/// <example>
	/// 我们先来看看一个最简单的例子，如何进行实例化的操作。
	/// <code lang="cs" source="HslCommunication_Net45.Test\Documentation\Samples\Enthernet\HttpServerSample.cs" region="Sample1" title="基本的实例化" />
	/// 通常来说，基本的实例化，返回固定的数据并不能满足我们的需求，我们需要返回自定义的数据，有一个委托，我们需要自己指定方法.
	/// <code lang="cs" source="HslCommunication_Net45.Test\Documentation\Samples\Enthernet\HttpServerSample.cs" region="Sample2" title="自定义返回" />
	/// 我们实际的需求可能会更加的复杂，不同的网址会返回不同的数据，所以接下来我们需要对网址信息进行判断。
	/// <code lang="cs" source="HslCommunication_Net45.Test\Documentation\Samples\Enthernet\HttpServerSample.cs" region="Sample3" title="区分网址" />
	/// 如果我们想增加安全性的验证功能，比如我们的api接口需要增加用户名和密码的功能，那么我们也可以实现
	/// <code lang="cs" source="HslCommunication_Net45.Test\Documentation\Samples\Enthernet\HttpServerSample.cs" region="Sample4" title="安全实现" />
	/// 当然了，如果我们想反回一个完整的html网页，也是可以实现的，甚至添加一些js的脚本，下面的例子就简单的说明了如何操作
	/// <code lang="cs" source="HslCommunication_Net45.Test\Documentation\Samples\Enthernet\HttpServerSample.cs" region="Sample5" title="返回html" />
	/// 如果需要实现跨域的操作，可以将属性<see cref="P:HslCommunication.Enthernet.HttpServer.IsCrossDomain" /> 设置为<c>True</c>
	/// </example>
	// Token: 0x020000F8 RID: 248
	public class HttpServer
	{
		/// <summary>
		/// 实例化一个默认的对象，当前的运行，需要使用管理员的模式运行<br />
		/// Instantiate a default object, the current operation, you need to use the administrator mode to run
		/// </summary>
		// Token: 0x060014EF RID: 5359 RVA: 0x0006C798 File Offset: 0x0006A998
		public HttpServer()
		{
			this.statisticsDict = new LogStatisticsDict(GenerateMode.ByEveryDay, 60);
			this.apiTopicServiceDict = new Dictionary<string, MqttRpcApiInfo>();
			this.rpcApiLock = new object();
		}

		/// <summary>
		/// 启动服务器，正常调用该方法时，应该使用try...catch...来捕获错误信息<br />
		/// Start the server and use try...catch... to capture the error message when calling this method normally
		/// </summary>
		/// <param name="port">端口号信息</param>
		/// <exception cref="T:System.Net.HttpListenerException"></exception>
		/// <exception cref="T:System.ObjectDisposedException"></exception>
		// Token: 0x060014F0 RID: 5360 RVA: 0x0006C7F0 File Offset: 0x0006A9F0
		public void Start(int port)
		{
			this.port = port;
			this.listener = new HttpListener();
			this.listener.Prefixes.Add(string.Format("http://+:{0}/", port));
			this.listener.Start();
			this.listener.BeginGetContext(new AsyncCallback(this.GetConnectCallBack), this.listener);
			ILogNet logNet = this.logNet;
			if (logNet != null)
			{
				logNet.WriteDebug(this.ToString(), "Server Started, wait for connections");
			}
		}

		/// <summary>
		/// 关闭服务器<br />
		/// Shut down the server
		/// </summary>
		// Token: 0x060014F1 RID: 5361 RVA: 0x0006C878 File Offset: 0x0006AA78
		public void Close()
		{
			HttpListener httpListener = this.listener;
			if (httpListener != null)
			{
				httpListener.Close();
			}
		}

		// Token: 0x060014F2 RID: 5362 RVA: 0x0006C890 File Offset: 0x0006AA90
		[DebuggerStepThrough]
		private void GetConnectCallBack(IAsyncResult ar)
		{
			HttpServer.<GetConnectCallBack>d__3 <GetConnectCallBack>d__ = new HttpServer.<GetConnectCallBack>d__3();
			<GetConnectCallBack>d__.<>t__builder = AsyncVoidMethodBuilder.Create();
			<GetConnectCallBack>d__.<>4__this = this;
			<GetConnectCallBack>d__.ar = ar;
			<GetConnectCallBack>d__.<>1__state = -1;
			<GetConnectCallBack>d__.<>t__builder.Start<HttpServer.<GetConnectCallBack>d__3>(ref <GetConnectCallBack>d__);
		}

		// Token: 0x060014F3 RID: 5363 RVA: 0x0006C8D0 File Offset: 0x0006AAD0
		private string GetDataFromRequest(HttpListenerRequest request)
		{
			string result;
			try
			{
				List<byte> list = new List<byte>();
				byte[] array = new byte[this.receiveBufferSize];
				int num = 0;
				int num2;
				do
				{
					num2 = request.InputStream.Read(array, 0, array.Length);
					num += num2;
					list.AddRange(SoftBasic.ArraySelectBegin<byte>(array, num2));
				}
				while (num2 != 0);
				result = this.encoding.GetString(list.ToArray(), 0, num);
			}
			catch
			{
				result = string.Empty;
			}
			return result;
		}

		// Token: 0x060014F4 RID: 5364 RVA: 0x0006C958 File Offset: 0x0006AB58
		[DebuggerStepThrough]
		private Task<string> GetDataFromRequestAsync(HttpListenerRequest request)
		{
			HttpServer.<GetDataFromRequestAsync>d__5 <GetDataFromRequestAsync>d__ = new HttpServer.<GetDataFromRequestAsync>d__5();
			<GetDataFromRequestAsync>d__.<>t__builder = AsyncTaskMethodBuilder<string>.Create();
			<GetDataFromRequestAsync>d__.<>4__this = this;
			<GetDataFromRequestAsync>d__.request = request;
			<GetDataFromRequestAsync>d__.<>1__state = -1;
			<GetDataFromRequestAsync>d__.<>t__builder.Start<HttpServer.<GetDataFromRequestAsync>d__5>(ref <GetDataFromRequestAsync>d__);
			return <GetDataFromRequestAsync>d__.<>t__builder.Task;
		}

		/// <summary>
		/// 根据客户端的请求进行处理的核心方法，可以返回自定义的数据内容，只需要集成重写即可。<br />
		/// The core method of processing according to the client's request can return custom data content, and only needs to be integrated and rewritten.
		/// </summary>
		/// <param name="request">请求</param>
		/// <param name="response">回应</param>
		/// <param name="data">Body数据</param>
		/// <returns>返回的内容</returns>
		// Token: 0x060014F5 RID: 5365 RVA: 0x0006C9A4 File Offset: 0x0006ABA4
		protected virtual string HandleRequest(HttpListenerRequest request, HttpListenerResponse response, string data)
		{
			bool flag = request.HttpMethod == "HSL";
			string result;
			if (flag)
			{
				bool flag2 = request.RawUrl.StartsWith("/Apis");
				if (flag2)
				{
					response.AddHeader("Content-type", "Content-Type: application/json; charset=utf-8");
					result = this.GetAllRpcApiInfo().ToJsonString(1);
				}
				else
				{
					bool flag3 = request.RawUrl.StartsWith("/Logs");
					if (flag3)
					{
						response.AddHeader("Content-type", "Content-Type: application/json; charset=utf-8");
						bool flag4 = request.RawUrl == "/Logs" || request.RawUrl == "/Logs/";
						if (flag4)
						{
							result = this.LogStatistics.LogStat.GetStatisticsSnapshot().ToJsonString(1);
						}
						else
						{
							result = this.LogStatistics.GetStatisticsSnapshot(request.RawUrl.Substring(6)).ToJsonString(1);
						}
					}
					else
					{
						response.AddHeader("Content-type", "Content-Type: application/json; charset=utf-8");
						result = this.GetAllRpcApiInfo().ToJsonString(1);
					}
				}
			}
			else
			{
				MqttRpcApiInfo mqttRpcApiInfo = this.GetMqttRpcApiInfo(HttpServer.GetMethodName(request.RawUrl));
				bool flag5 = mqttRpcApiInfo == null;
				if (flag5)
				{
					bool flag6 = this.HandleRequestFunc != null;
					if (flag6)
					{
						result = this.HandleRequestFunc(request, response, data);
					}
					else
					{
						result = "This is HslWebServer, Thank you for use!";
					}
				}
				else
				{
					DateTime now = DateTime.Now;
					string text = HttpServer.HandleObjectMethod(request, data, mqttRpcApiInfo);
					double num = Math.Round((DateTime.Now - now).TotalSeconds, 5);
					mqttRpcApiInfo.CalledCountAddOne((long)(num * 100000.0));
					this.statisticsDict.StatisticsAdd(mqttRpcApiInfo.ApiTopic, 1L);
					ILogNet logNet = this.LogNet;
					if (logNet != null)
					{
						logNet.WriteDebug(this.ToString(), string.Format("[{0}] HttpRpc request:[{1}] Spend:[{2:F2} ms] Count:[{3}]", new object[]
						{
							request.RemoteEndPoint,
							mqttRpcApiInfo.ApiTopic,
							num * 1000.0,
							mqttRpcApiInfo.CalledCount
						}));
					}
					result = text;
				}
			}
			return result;
		}

		/// <summary>
		/// 获取当前的日志统计信息，可以获取到每个API的每天的调度次数信息，缓存60天数据，如果需要存储本地，需要调用<see cref="M:HslCommunication.LogNet.LogStatisticsDict.SaveToFile(System.String)" />方法。<br />
		/// Get the current log statistics, you can get the daily scheduling times information of each API, and cache 60-day data. 
		/// If you need to store it locally, you need to call the <see cref="M:HslCommunication.LogNet.LogStatisticsDict.SaveToFile(System.String)" /> method.
		/// </summary>
		// Token: 0x170004F3 RID: 1267
		// (get) Token: 0x060014F6 RID: 5366 RVA: 0x0006CBBB File Offset: 0x0006ADBB
		public LogStatisticsDict LogStatistics
		{
			get
			{
				return this.statisticsDict;
			}
		}

		/// <inheritdoc cref="P:HslCommunication.Core.Net.NetworkBase.LogNet" />
		// Token: 0x170004F4 RID: 1268
		// (get) Token: 0x060014F7 RID: 5367 RVA: 0x0006CBC3 File Offset: 0x0006ADC3
		// (set) Token: 0x060014F8 RID: 5368 RVA: 0x0006CBCB File Offset: 0x0006ADCB
		public ILogNet LogNet
		{
			get
			{
				return this.logNet;
			}
			set
			{
				this.logNet = value;
			}
		}

		/// <summary>
		/// 获取或设置当前服务器的编码信息，默认为UTF8编码<br />
		/// Get or set the encoding information of the current server, the default is UTF8 encoding
		/// </summary>
		// Token: 0x170004F5 RID: 1269
		// (get) Token: 0x060014F9 RID: 5369 RVA: 0x0006CBD4 File Offset: 0x0006ADD4
		// (set) Token: 0x060014FA RID: 5370 RVA: 0x0006CBDC File Offset: 0x0006ADDC
		public Encoding ServerEncoding
		{
			get
			{
				return this.encoding;
			}
			set
			{
				this.encoding = value;
			}
		}

		/// <summary>
		/// 获取或设置是否支持跨域操作<br />
		/// Get or set whether to support cross-domain operations
		/// </summary>
		// Token: 0x170004F6 RID: 1270
		// (get) Token: 0x060014FB RID: 5371 RVA: 0x0006CBE5 File Offset: 0x0006ADE5
		// (set) Token: 0x060014FC RID: 5372 RVA: 0x0006CBED File Offset: 0x0006ADED
		public bool IsCrossDomain { get; set; }

		/// <summary>
		/// 获取或设置当前的自定义的处理信息，如果不想继承实现方法，可以使用本属性来关联你自定义的方法。<br />
		/// Get or set the current custom processing information. If you don't want to inherit the implementation method, you can use this attribute to associate your custom method.
		/// </summary>
		// Token: 0x170004F7 RID: 1271
		// (get) Token: 0x060014FD RID: 5373 RVA: 0x0006CBF6 File Offset: 0x0006ADF6
		// (set) Token: 0x060014FE RID: 5374 RVA: 0x0006CBFE File Offset: 0x0006ADFE
		public Func<HttpListenerRequest, HttpListenerResponse, string, string> HandleRequestFunc
		{
			get
			{
				return this.handleRequestFunc;
			}
			set
			{
				this.handleRequestFunc = value;
			}
		}

		/// <summary>
		/// 获取当前的端口号信息<br />
		/// Get current port number information
		/// </summary>
		// Token: 0x170004F8 RID: 1272
		// (get) Token: 0x060014FF RID: 5375 RVA: 0x0006CC07 File Offset: 0x0006AE07
		public int Port
		{
			get
			{
				return this.port;
			}
		}

		// Token: 0x06001500 RID: 5376 RVA: 0x0006CC10 File Offset: 0x0006AE10
		private MqttRpcApiInfo GetMqttRpcApiInfo(string apiTopic)
		{
			MqttRpcApiInfo result = null;
			object obj = this.rpcApiLock;
			lock (obj)
			{
				bool flag2 = this.apiTopicServiceDict.ContainsKey(apiTopic);
				if (flag2)
				{
					result = this.apiTopicServiceDict[apiTopic];
				}
			}
			return result;
		}

		/// <summary>
		/// 获取当前所有注册的RPC接口信息，将返回一个数据列表。<br />
		/// Get all currently registered RPC interface information, and a data list will be returned.
		/// </summary>
		/// <returns>信息列表</returns>
		// Token: 0x06001501 RID: 5377 RVA: 0x0006CC78 File Offset: 0x0006AE78
		public MqttRpcApiInfo[] GetAllRpcApiInfo()
		{
			MqttRpcApiInfo[] result = null;
			object obj = this.rpcApiLock;
			lock (obj)
			{
				result = Enumerable.ToArray<MqttRpcApiInfo>(this.apiTopicServiceDict.Values);
			}
			return result;
		}

		/// <summary>
		/// 注册一个RPC的服务接口，可以指定当前的控制器名称，以及提供RPC服务的原始对象<br />
		/// Register an RPC service interface, you can specify the current controller name, 
		/// and the original object that provides the RPC service
		/// </summary>
		/// <param name="api">前置的接口信息，可以理解为MVC模式的控制器</param>
		/// <param name="obj">原始对象信息</param>
		// Token: 0x06001502 RID: 5378 RVA: 0x0006CCD0 File Offset: 0x0006AED0
		public void RegisterHttpRpcApi(string api, object obj)
		{
			object obj2 = this.rpcApiLock;
			lock (obj2)
			{
				foreach (MqttRpcApiInfo mqttRpcApiInfo in MqttHelper.GetSyncServicesApiInformationFromObject(api, obj, null))
				{
					this.apiTopicServiceDict.Add(mqttRpcApiInfo.ApiTopic, mqttRpcApiInfo);
				}
			}
		}

		/// <inheritdoc cref="M:HslCommunication.Enthernet.HttpServer.RegisterHttpRpcApi(System.String,System.Object)" />
		// Token: 0x06001503 RID: 5379 RVA: 0x0006CD64 File Offset: 0x0006AF64
		public void RegisterHttpRpcApi(object obj)
		{
			object obj2 = this.rpcApiLock;
			lock (obj2)
			{
				foreach (MqttRpcApiInfo mqttRpcApiInfo in MqttHelper.GetSyncServicesApiInformationFromObject(obj))
				{
					this.apiTopicServiceDict.Add(mqttRpcApiInfo.ApiTopic, mqttRpcApiInfo);
				}
			}
		}

		/// <inheritdoc />
		// Token: 0x06001504 RID: 5380 RVA: 0x0006CDF8 File Offset: 0x0006AFF8
		public override string ToString()
		{
			return string.Format("HttpServer[{0}]", this.port);
		}

		/// <summary>
		/// 使用指定的对象来返回网络的API接口，前提是传入的数据为json参数，返回的数据为json数据，详细参照说明<br />
		/// Use the specified object to return the API interface of the network, 
		/// provided that the incoming data is json parameters and the returned data is json data, 
		/// please refer to the description for details
		/// </summary>
		/// <param name="request">当前的请求信息</param>
		/// <param name="json">json格式的参数信息</param>
		/// <param name="obj">等待解析的api解析的对象</param>
		/// <returns>等待返回客户的结果</returns>
		// Token: 0x06001505 RID: 5381 RVA: 0x0006CE10 File Offset: 0x0006B010
		public static string HandleObjectMethod(HttpListenerRequest request, string json, object obj)
		{
			string text = HttpServer.GetMethodName(request.RawUrl);
			bool flag = text.LastIndexOf('/') >= 0;
			if (flag)
			{
				text = text.Substring(text.LastIndexOf('/') + 1);
			}
			MethodInfo method = obj.GetType().GetMethod(text);
			bool flag2 = method == null;
			string result;
			if (flag2)
			{
				result = new OperateResult<string>("Current MqttSync Api ：[" + text + "] not exsist").ToJsonString(1);
			}
			else
			{
				OperateResult<MqttRpcApiInfo> mqttSyncServicesApiFromMethod = MqttHelper.GetMqttSyncServicesApiFromMethod("", method, obj, null);
				bool flag3 = !mqttSyncServicesApiFromMethod.IsSuccess;
				if (flag3)
				{
					result = OperateResult.CreateFailedResult<string>(mqttSyncServicesApiFromMethod).ToJsonString(1);
				}
				else
				{
					result = HttpServer.HandleObjectMethod(request, json, mqttSyncServicesApiFromMethod.Content);
				}
			}
			return result;
		}

		/// <summary>
		/// 根据完整的地址获取当前的url地址信息
		/// </summary>
		/// <param name="url">地址信息</param>
		/// <returns>方法名称</returns>
		// Token: 0x06001506 RID: 5382 RVA: 0x0006CEC8 File Offset: 0x0006B0C8
		public static string GetMethodName(string url)
		{
			string text = string.Empty;
			bool flag = url.IndexOf('?') > 0;
			if (flag)
			{
				text = url.Substring(0, url.IndexOf('?'));
			}
			else
			{
				text = url;
			}
			bool flag2 = text.EndsWith("/") || text.StartsWith("/");
			if (flag2)
			{
				text = text.Trim(new char[]
				{
					'/'
				});
			}
			return text;
		}

		/// <summary>
		/// 使用指定的对象来返回网络的API接口，前提是传入的数据为json参数，返回的数据为json数据，详细参照说明<br />
		/// Use the specified object to return the API interface of the network, 
		/// provided that the incoming data is json parameters and the returned data is json data, 
		/// please refer to the description for details
		/// </summary>
		/// <param name="request">当前的请求信息</param>
		/// <param name="json">json格式的参数信息</param>
		/// <param name="apiInformation">等待解析的api解析的对象</param>
		/// <returns>等待返回客户的结果</returns>
		// Token: 0x06001507 RID: 5383 RVA: 0x0006CF38 File Offset: 0x0006B138
		public static string HandleObjectMethod(HttpListenerRequest request, string json, MqttRpcApiInfo apiInformation)
		{
			bool flag = apiInformation.PermissionAttribute != null;
			if (flag)
			{
				bool flag2 = Authorization.asdniasnfaksndiqwhawfskhfaiw();
				if (!flag2)
				{
					return new OperateResult<string>("Permission function need authorization ：" + StringResources.Language.InsufficientPrivileges).ToJsonString(1);
				}
				try
				{
					string[] values = request.Headers.GetValues("Authorization");
					bool flag3 = values == null || values.Length < 1 || string.IsNullOrEmpty(values[0]);
					if (flag3)
					{
						return new OperateResult<string>("Mqtt RPC Api ：[" + apiInformation.ApiTopic + "] has none Authorization information, access not permission").ToJsonString(1);
					}
					string s = values[0].Split(new char[]
					{
						' '
					}, StringSplitOptions.RemoveEmptyEntries)[1];
					string @string = Encoding.UTF8.GetString(Convert.FromBase64String(s));
					string[] array = @string.Split(new char[]
					{
						':'
					}, StringSplitOptions.RemoveEmptyEntries);
					bool flag4 = array.Length < 1;
					if (flag4)
					{
						return new OperateResult<string>("Mqtt RPC Api ：[" + apiInformation.ApiTopic + "] has none Username information, access not permission").ToJsonString(1);
					}
					bool flag5 = !apiInformation.PermissionAttribute.CheckUserName(array[0]);
					if (flag5)
					{
						return new OperateResult<string>(string.Concat(new string[]
						{
							"Mqtt RPC Api ：[",
							apiInformation.ApiTopic,
							"] Check Username[",
							array[0],
							"] failed, access not permission"
						})).ToJsonString(1);
					}
				}
				catch (Exception ex)
				{
					return new OperateResult<string>("Mqtt RPC Api ：[" + apiInformation.ApiTopic + "] Check Username failed, access not permission, reason:" + ex.Message).ToJsonString(1);
				}
			}
			string result;
			try
			{
				bool flag6 = apiInformation.Method != null;
				if (flag6)
				{
					MethodInfo method = apiInformation.Method;
					string apiTopic = apiInformation.ApiTopic;
					bool flag7 = request.HttpMethod != apiInformation.HttpMethod;
					if (flag7)
					{
						result = new OperateResult("Current Api ：" + apiTopic + " not support diffrent httpMethod").ToJsonString(1);
					}
					else
					{
						bool flag8 = request.HttpMethod == "POST";
						object[] parameters;
						if (flag8)
						{
							parameters = HslReflectionHelper.GetParametersFromJson(method.GetParameters(), json);
						}
						else
						{
							bool flag9 = request.HttpMethod == "GET";
							if (!flag9)
							{
								return new OperateResult("Current Api ：" + apiTopic + " not support GET or POST").ToJsonString(1);
							}
							bool flag10 = request.RawUrl.IndexOf('?') > 0;
							if (flag10)
							{
								parameters = HslReflectionHelper.GetParametersFromUrl(method.GetParameters(), request.RawUrl);
							}
							else
							{
								parameters = HslReflectionHelper.GetParametersFromJson(method.GetParameters(), json);
							}
						}
						result = method.Invoke(apiInformation.SourceObject, parameters).ToJsonString(1);
					}
				}
				else
				{
					bool flag11 = apiInformation.Property != null;
					if (flag11)
					{
						string apiTopic2 = apiInformation.ApiTopic;
						bool flag12 = request.HttpMethod != apiInformation.HttpMethod;
						if (flag12)
						{
							result = new OperateResult("Current Api ：" + apiTopic2 + " not support diffrent httpMethod").ToJsonString(1);
						}
						else
						{
							bool flag13 = request.HttpMethod != "GET";
							if (flag13)
							{
								result = new OperateResult("Current Api ：" + apiTopic2 + " not support POST").ToJsonString(1);
							}
							else
							{
								result = apiInformation.Property.GetValue(apiInformation.SourceObject, null).ToJsonString(1);
							}
						}
					}
					else
					{
						result = new OperateResult("Current Api ：" + request.RawUrl + " not supported").ToJsonString(1);
					}
				}
			}
			catch (Exception ex2)
			{
				result = new OperateResult("Current Api ：" + request.RawUrl + " Wrong，Reason：" + ex2.Message).ToJsonString(1);
			}
			return result;
		}

		// Token: 0x040004F5 RID: 1269
		private Dictionary<string, MqttRpcApiInfo> apiTopicServiceDict;

		// Token: 0x040004F6 RID: 1270
		private object rpcApiLock;

		// Token: 0x040004F7 RID: 1271
		private int receiveBufferSize = 2048;

		// Token: 0x040004F8 RID: 1272
		private int port = 80;

		// Token: 0x040004F9 RID: 1273
		private HttpListener listener;

		// Token: 0x040004FA RID: 1274
		private ILogNet logNet;

		// Token: 0x040004FB RID: 1275
		private Encoding encoding = Encoding.UTF8;

		// Token: 0x040004FC RID: 1276
		private Func<HttpListenerRequest, HttpListenerResponse, string, string> handleRequestFunc;

		// Token: 0x040004FD RID: 1277
		private LogStatisticsDict statisticsDict;
	}
}
